home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / PROGRAMM / CC_C / 0335.ZIP / PASTE.C < prev    next >
Text File  |  1985-05-08  |  12KB  |  492 lines

  1. /*
  2.  * paste - a recreation of the Unix(Tm) paste(1) command.
  3.  *
  4.  * syntax:    paste file1 file2 ...
  5.  *        paste -dLIST file1 file2 ...
  6.  *        paste -s [-dLIST] file1 file2 ...
  7.  *
  8.  *    Copyright (C) 1984 by David M. Ihnat
  9.  *
  10.  * This program is a total rewrite of the Bell Laboratories Unix(Tm)
  11.  * command of the same name, as of System V.  It contains no proprietary
  12.  * code, and therefore may be used without violation of any proprietary
  13.  * agreements whatsoever.  However, you will notice that the program is
  14.  * copyrighted by me.  This is to assure the program does *not* fall
  15.  * into the public domain.  Thus, I may specify just what I am now:
  16.  * This program may be freely copied and distributed, provided this notice
  17.  * remains; it may not be sold for profit without express written consent of
  18.  * the author.
  19.  * Please note that I recreated the behavior of the Unix(Tm) 'paste' command
  20.  * as faithfully as possible, with minor exceptions (noted below); however,
  21.  * I haven't run a full set of regression * tests.  Thus, the user of
  22.  * this program accepts full responsibility for any effects or loss;
  23.  * in particular, the author is not responsible for any losses,
  24.  * explicit or incidental, that may be incurred through use of this program.
  25.  *
  26.  * The changes to the program, with one exception, are transparent to
  27.  * a user familiar with the Unix command of the same name.  These changes
  28.  * are:
  29.  *
  30.  * 1) The '-s' option had a bug in the Unix version when used with multiple
  31.  *    files.  (It would repeat each file in a list, i.e., for
  32.  *    'paste -s file1 file2 file3', it would list
  33.  *    <file1\n><file1\n><file2\n><file1\n><file2\n><file3\n>
  34.  *    I fixed this, and reported the bug to the providers of the command in
  35.  *    Unix.
  36.  *
  37.  * 2) The list of valid escape sequences has been expanded to include
  38.  *    \b,\f,\r, and \v.  (Just because *I* can't imagine why you'd want
  39.  *    to use them doesn't mean I should keep them from you.)
  40.  *
  41.  * 3) There is no longer any restriction on line length.
  42.  *
  43.  * I ask that any bugs (and, if possible, fixes) be reported to me when
  44.  * possible.  -David Ihnat (312) 784-4544 ihuxx!ignatz
  45.  */
  46.  
  47. #include <stdio.h>
  48.  
  49. extern int errno;
  50.  
  51. #define CPM
  52.  
  53. /* I'd love to use enums, but not everyone has them.  Portability, y'know. */
  54. #define NODELIM        1
  55. #define USAGE        2
  56. #define BADFILE        3
  57. #define TOOMANY        4
  58.  
  59. #define    TAB        '\t'
  60. #define    NL        '\n'
  61. #define    BS        '\b'
  62. #define    FF        '\f'
  63. #define    CR        '\r'
  64. #define    VT        '\v'
  65. #define    DEL        '\177'
  66.  
  67. #define    _MAXSZ        512
  68. #define _MAXFILES    12
  69. #define    CLOSED        ((FILE *)-1)
  70. #define ENDLIST        ((FILE *)-2)
  71. #define COMMAND        "paste"
  72.  
  73. char *cmdnam;
  74.  
  75. short int dflag,sflag;
  76. char delims[] = { TAB };
  77.  
  78. main(argc,argv)
  79. int argc;
  80. char **argv;
  81. {
  82.     char *strcpy();
  83.  
  84.     dflag = sflag = 0;
  85.  
  86. #ifdef CPM
  87.     cmdnam = COMMAND;
  88. #else
  89.     cmdnam = *argv;
  90. #endif
  91.  
  92.     /* Skip invocation name */
  93.     argv++;
  94.     argc--;
  95.  
  96.     /* First, parse input options */
  97.  
  98.     while(argv[0][0] == '-' && argv[0][1] != '\0')
  99.     {
  100.         switch(argv[0][1])
  101.         {
  102.             case 'd':
  103.             case 'D':
  104.                 /* Delimiter character(s) */
  105.                 strcpy(delims,&argv[0][2]);
  106.                 if(*delims == '\0')
  107.                     prerr(NODELIM,"");
  108.                 else
  109.                     delimbuild(delims);
  110.  
  111.                 break;
  112.  
  113.             case 's':
  114.             case 'S':
  115.                 sflag++;
  116.                 break;
  117.  
  118.             default:
  119.                 prerr(USAGE,"");
  120.         }
  121.         argv++;
  122.         argc--;
  123.     }
  124.  
  125.     /*
  126.      * If no files specified, simply exit.  Otherwise,
  127.      * if not the old '-s' option, process all files.
  128.      * If '-s', then process files one-at-a-time.
  129.      */
  130.     if(!sflag)
  131.         docol(argc,argv); /* Column paste */
  132.     else
  133.         doserial(argc,argv);    /* Serial paste */
  134.  
  135.  
  136. docol(nfiles,fnamptr)
  137. int nfiles;
  138. char **fnamptr;
  139. {
  140.     char iobuff[_MAXSZ];    /* i/o buffer for the fgets */
  141.     short int somedone;    /* flag for blank field handling */
  142.  
  143.     /*
  144.      * There is a strange case where all files are just ready to
  145.      * be closed, or will on this round.  In that case, the string
  146.      * of delimiters must be preserved.  delbuf[1] ->delbuf[MAXFILES+1]
  147.      * provides intermediate storage for closed files, if needed;
  148.      * delbuf[0] is the current index.
  149.      */
  150.     char delbuf[_MAXFILES+2];
  151.  
  152.     FILE *fileptr[_MAXFILES+1];
  153.     FILE *fopen();
  154.  
  155.     char *fgets();
  156.  
  157.     int filecnt;            /* Set to number of files to process */
  158.     register char *delimptr;    /* Cycling delimiter pointer */
  159.     int index;            /* Working variable */
  160.     int strend;            /* End of string in buffer */
  161.  
  162.     /*
  163.      * Perform column paste.  First, attempt to open all
  164.      * files. (This could be expanded to an infinite number of files,
  165.      * but at the (considerable) expense of remembering the file and
  166.      * its current offset, then opening/reading/closing.  The
  167.      * commands' utility doesn't warrant the effort; at least, to me...)
  168.      */
  169.  
  170.     for(filecnt=0;(nfiles>0) && (filecnt<_MAXFILES);filecnt++,nfiles--,fnamptr++)
  171.     {
  172.         if(fnamptr[0][0] == '-')
  173.             fileptr[filecnt] = stdin;
  174.         else
  175.         {
  176.             fileptr[filecnt] = fopen(*fnamptr,"r");
  177.             if(fileptr[filecnt] == NULL)
  178.                 prerr(BADFILE,*fnamptr);
  179.         }
  180.     }
  181.  
  182.     fileptr[filecnt] = ENDLIST;    /* End of list. */
  183.  
  184.     if(nfiles)
  185.         prerr(TOOMANY,"");
  186.  
  187.     /*
  188.      * Have all files.  Now, read a line from each file, and output
  189.      * to stdout.  Notice that the old 511 character limitation on
  190.      * the line length no longer applies, since this program doesn't
  191.      * do the buffering.  Do this until you go through the loop and
  192.      * don't successfully read from any of the files.
  193.      */
  194.     for(;filecnt;)
  195.     {
  196.         somedone = 0;        /* Blank field handling flag */
  197.         delimptr = delims;    /* Start at beginning of delim list */
  198.         delbuf[0] = 0;        /* No squirreled delims */
  199.  
  200.         for(index = 0;(fileptr[index] != ENDLIST) && filecnt; index++)
  201.         {
  202.             /* Read a line and immediately output.
  203.              * If it's too big for the buffer, then dump what was
  204.              * read and go back for more.
  205.              *
  206.              * Otherwise, if it is from the last file, then leave
  207.              * the carriage return in place; if not, replace with
  208.              * a delimiter (if any)
  209.              */
  210.  
  211.             strend = 0;    /* Set so can easily detect EOF */
  212.  
  213.             if(fileptr[index] != CLOSED)
  214.  
  215.                 while(fgets(iobuff,(_MAXSZ-1),fileptr[index]) != (char *)NULL)
  216.                 {
  217.                     strend = strlen(iobuff);    /* Did the buffer fill? */
  218.  
  219.                     if(strend == (_MAXSZ-1))
  220.                     {
  221.                         /* Gosh, what a long line. */
  222.                         fputs(iobuff,stdout);
  223.                         strend = 0;
  224.                         continue;
  225.                     }
  226.  
  227.                     /* Ok got whole line in buffer. */
  228.                     break;    /* Out of loop for this file */
  229.                 }
  230.  
  231.             /* Ended either on an EOF (well, actually NULL return--
  232.              * it *could* be some sort of file error, but
  233.              * but if the file was opened successfully, this is
  234.              * unlikely. Besides, error checking on streams
  235.              * doesn't allow us to decide exactly what went
  236.              * wrong, so I'm going to be very Unix-like and ignore
  237.              * it!), or a closed file, or a received line.
  238.              * If an EOF, close the file and mark it in the
  239.              * list.  In any case, output the delimiter of choice.
  240.              */
  241.  
  242.             if(!strend)
  243.             {
  244.                 if(fileptr[index] != CLOSED)
  245.                 {
  246.                     fclose(fileptr[index]);
  247.                     fileptr[index] = CLOSED;
  248.                     filecnt--;
  249.                 }
  250.  
  251.                 /* Is this the end of the whole thing? */
  252.                 if((fileptr[index+1] == ENDLIST) && !somedone)
  253.                     continue; /* EXITS */
  254.  
  255.                 /* Ok, some files not closed this line. Last file? */
  256.                 if(fileptr[index+1] == ENDLIST)
  257.                 {
  258.                     if(delbuf[0])
  259.                     {
  260.                         fputs(&delbuf[1],stdout);
  261.                         delbuf[0] = 0;
  262.                     }
  263.                     putc((int)NL,stdout);
  264.                     continue;    /* Next read of files */
  265.                 }else
  266.                 {
  267.                     /* Closed file; setup delim */
  268.                     if(*delimptr != DEL)
  269.                     {
  270.                         delbuf[0]++;
  271.                         delbuf[delbuf[0]] = *delimptr++;
  272.                         delbuf[delbuf[0]+1] = '\0';
  273.                     }else
  274.                         delimptr++;
  275.                 }
  276.  
  277.                 /* Reset end of delimiter string if necessary */
  278.                 if(*delimptr == '\0')
  279.                     delimptr = delims;
  280.             }else
  281.             {
  282.                 /* Some data read. */
  283.                 somedone++;
  284.  
  285.                 /* Any saved delims? */
  286.                 if(delbuf[0])
  287.                 {
  288.                     fputs(&delbuf[1],stdout);
  289.                     delbuf[0] = 0;
  290.                 }
  291.  
  292.                 /* If last file, last char will be NL. */
  293.                 if(fileptr[index+1] != ENDLIST)
  294.                 {
  295.                     if(*delimptr == DEL)
  296.                     {
  297.                         delimptr++;
  298.                         iobuff[strend-1] = '\0'; /* No delim */
  299.                     }else
  300.                         iobuff[strend-1] = *delimptr++;
  301.                 }
  302.  
  303.                 if(*delimptr == '\0')
  304.                     delimptr = delims;
  305.  
  306.                 /* Now dump the buffer */
  307.                 fputs(iobuff,stdout);
  308.             }
  309.         }
  310.     }
  311. }
  312.  
  313. doserial(nfiles,fnamptr)
  314. int nfiles;
  315. char **fnamptr;
  316. {
  317.     /*
  318.      * Do serial paste.  Simply scarf characters, performing
  319.      * one-character buffering to facilitate delim processing.
  320.      */
  321.     
  322.     register int charnew,charold;
  323.     register char *delimptr;
  324.  
  325.     register FILE *fileptr;
  326.     FILE *fopen();
  327.  
  328.     for(;nfiles;nfiles--,fnamptr++)
  329.     {
  330.         if(fnamptr[0][0] == '-')
  331.             fileptr = stdin;
  332.         else
  333.         {
  334.             fileptr = fopen(*fnamptr,"r");
  335.  
  336.             if(fileptr == NULL)
  337.                 prerr(BADFILE,*fnamptr);
  338.         }
  339.  
  340.         /*
  341.          * The file is open; just keep taking characters,
  342.          * stashing them in charnew; output charold, converting to
  343.          * the appropriate delimiter character if needful.  After the
  344.          * EOF, simply output 'charold' if it's a newline; otherwise,
  345.          * output it and then a newline.
  346.          */
  347.  
  348.         delimptr = delims;    /* Set up for delimiter string */
  349.  
  350.         if((charold = getc(fileptr)) == EOF)
  351.         {
  352.             /* Empty file! */
  353.             putc(NL,stdout);
  354.             continue;    /* Go on to the next file */
  355.         }
  356.  
  357.         /* Ok, 'charold' is set up.  Hit it! */
  358.  
  359.         while((charnew = getc(fileptr)) != EOF)
  360.         {
  361.             /* Ok, process the old character */
  362.             if(charold == NL)
  363.             {
  364.                 if(*delimptr != DEL)
  365.                     putc(*delimptr++,stdout);
  366.  
  367.                 /* Reset pointer at end of delimiter string */
  368.                 if(*delimptr == '\0')
  369.                     delimptr = delims;
  370.             } else
  371.                 putc((char)charold,stdout);
  372.             
  373.             charold = charnew;
  374.         }
  375.  
  376.         /* Ok, hit EOF.  Process that last character */
  377.  
  378.         putc((char)charold,stdout);
  379.  
  380.         if((char)charold != NL)
  381.             putc(NL,stdout);
  382.     }
  383. }
  384.  
  385. delimbuild(strptr)
  386. char *strptr;
  387. {
  388.     /*
  389.      * Process the delimiter string into something that can be
  390.      * used by the routines.  This involves, primarily, collapsing
  391.      * the backslash representations of special characters into
  392.      * their actual values, and terminating the string in a manner
  393.      * that the routines can recognize.  The set of possible backslash
  394.      * characters has been expanded beyond that recognized by the
  395.      * vanilla Unix(Tm) version.
  396.      */
  397.     register char *strout;
  398.  
  399.     strout = strptr;    /* Start at the same place, anyway */
  400.  
  401.     while(*strptr)
  402.     {
  403.         if(*strptr != '\\')    /* Is it an escape character? */
  404.             *strout++ = *strptr++;    /* No, just transfer it */
  405.         else
  406.         {
  407.             strptr++;    /* Get past escape character */
  408.             switch(*strptr)
  409.             {
  410.                 case '0':
  411.                     *strout++ = DEL;
  412.                     break;
  413.  
  414.                 case 't':
  415. #ifdef CPM
  416.                 case 'T':
  417. #endif
  418.                     *strout++ = TAB;
  419.                     break;
  420.  
  421.                 case 'n':
  422. #ifdef CPM
  423.                 case 'N':
  424. #endif
  425.                     *strout++ = NL;
  426.                     break;
  427.  
  428.                 case 'b':
  429. #ifdef CPM
  430.                 case 'B':
  431. #endif
  432.                     *strout++ = BS;
  433.                     break;
  434.  
  435.                 case 'f':
  436. #ifdef CPM
  437.                 case 'F':
  438. #endif
  439.                     *strout++ = FF;
  440.                     break;
  441.  
  442.                 case 'r':
  443. #ifdef CPM
  444.                 case 'R':
  445. #endif
  446.                     *strout++ = CR;
  447.                     break;
  448.  
  449.                 case 'v':
  450. #ifdef CPM
  451.                 case 'V':
  452. #endif
  453.                     *strout++ = VT;
  454.                     break;
  455.  
  456.                 default:
  457.                     *strout++ = *strptr;
  458.             }
  459.             
  460.             strptr++;
  461.         }
  462.  
  463.     }
  464.     *strout = '\0';    /* Heaven forfend that we forget this! */
  465. }
  466.  
  467. prerr(etype, estring)
  468. int etype;
  469. char *estring;
  470. {
  471.     switch(etype)
  472.     {
  473.         case USAGE:
  474.             fprintf(stderr,"%s : Usage: %s [-s] [-d<delimiterstring>] file1 file2 ...\n",cmdnam,cmdnam);
  475.             break;
  476.  
  477.         case NODELIM:
  478.             fprintf(stderr,"%s : no delimiters\n",cmdnam);
  479.             break;
  480.  
  481.         case BADFILE:
  482.             fprintf(stderr,"%s : %s : cannot open\n",cmdnam,estring);
  483.             break;
  484.         
  485.         case TOOMANY:
  486.             fprintf(stderr,"%s : too many files\n",cmdnam);
  487.             break;
  488.     }
  489.     exit(1);
  490. }
  491.